package com.geospatial.algorithm.service.index.impl;

import com.geospatial.algorithm.config.ApplicationConfig;
import com.geospatial.algorithm.service.index.GeoHashService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * geohash算法实现
 * @deprecated
 * @description geohash算法实现
 * @link https://halfrost.com/go_spatial_search/
 * @link https://www.open-open.com/lib/view/open1417940079964.html
 */
@Service
public class GeoHashServiceImpl implements GeoHashService {
    @Autowired
    private ApplicationConfig applicationConfig;
    private Map geohashCOnfig;

    @Override
    public String encodeGeoHash(double longitude, double latitude) {
        //base32串
        String BASE32 = (String) getGeohashCOnfig().get("BASE32");
        //二进制数字位移数,数组
        List<Integer> BITS = (List<Integer>)getGeohashCOnfig().get("BITS");
        //偶数放经度，奇数放纬度
        boolean is_even = true;
        //经纬度对比范围，与目标点对比,左，中，右范围
        double[] lat = new double[]{-90.0,0,90.0},
                 lon = new double[]{-180.0,0,180.0};
        //bit BITS数组下标, ch base32串下标, precision geohash串最大长度
        int bit = 0, ch = 0, precision = 12;
        String geohash = "";
        while (geohash.length() < precision) {
            //偶数放经度，奇数放纬度
            if (is_even) {
                //计算中坐标值
                lon[1] = (lon[0] + lon[2]) / 2;
                //目标经度是否大于中坐标值
                if (longitude > lon[1]) {
                    // |-'或'
                    ch |= BITS.get(bit);
                    lon[0] = lon[1];
                } else {
                    lon[2] = lon[1];
                }
            } else {
                lat[1] = (lat[0] + lat[2]) / 2;
                if (latitude > lat[1] ) {
                    ch |= BITS.get(bit);
                    lat[0] = lat[1];
                } else {
                    lat[2] = lat[1];
                }
            }

            is_even = !is_even;
            if (bit < 4) {
                bit++;
            } else {
                geohash += BASE32.charAt(ch);
                bit = 0;
                ch = 0;
            }

        }
        return geohash;
    }

    @Override
    public Map decodeGeoHash(String geohash) {
        Map map = new HashMap();
        //base32串
        String BASE32 = (String) getGeohashCOnfig().get("BASE32");
        //二进制数字位移数,数组
        List<Integer> BITS = (List<Integer>)getGeohashCOnfig().get("BITS");
        //偶数放经度，奇数放纬度
        boolean is_even = true;
        //经纬度对比范围，与目标点对比,左，中，右范围
        double[] lat = new double[3],
                lon = new double[3];
        lat[0] = -90.0;
        lat[1] = 90.0;
        lon[0] = -180.0;
        lon[1] = 180.0;
        double lat_err = 90.0,lon_err = 180.0;
        char ch;
        int base32Position = 0;

        for (int i = 0; i < geohash.length(); i++) {
            ch = geohash.charAt(i);
            base32Position = BASE32.indexOf(ch);
            for (int j = 0; j < BITS.size(); j++) {
                int mask = BITS.get(j);
                if (is_even) {
                    lon_err /= 2;
                    lon = refineInterval(lon, base32Position, mask);
                } else {
                    lat_err /= 2;
                    lat = refineInterval(lat, base32Position, mask);
                }
                is_even = !is_even;
            }

        }
        lat[2] = (lat[0] + lat[1]) / 2;
        lon[2] = (lon[0] + lon[1]) / 2;
        map.put("latitude",lat);
        map.put("longitude",lon);
        return map;
    }

    @Override
    public String calculateAdjacent(String geohash, String direction) {
        //base32串
        String BASE32 = (String) getGeohashCOnfig().get("BASE32");
        geohash = geohash.toLowerCase();
        char lastChr = geohash.charAt(geohash.length() - 1);
        String type = (geohash.length() % 2) != 0 ? "odd" : "even";
        String base = geohash.substring(0, geohash.length() - 1);
        Map BORDERS = (Map) this.geohashCOnfig.get("BORDERS");
        Map NEIGHBORS = (Map) this.geohashCOnfig.get("NEIGHBORS");
        if (((String)((Map)BORDERS.get(direction)).get(type)).indexOf(lastChr) != -1)
            base = calculateAdjacent(base, type);
        return base + new Character(BASE32.charAt(((String)((Map)NEIGHBORS.get(direction)).get(type)).indexOf(lastChr))).toString();
    }

    /**
     *
     * @param interval
     * @param base32Position
     * @param bitMask
     * @return
     */
    private double[] refineInterval(double[] interval,int base32Position,int bitMask) {
        //&,与,同1出1,有0为0.
        if ((base32Position & bitMask) > 0) {
            interval[0] = (interval[0] + interval[1]) / 2;
        } else {
            interval[1] = (interval[0] + interval[1]) / 2;
        }
        return interval;
    }

    /**
     * 获取geohash配置
     * @return
     */
    private Map getGeohashCOnfig() {
        if (null == this.geohashCOnfig) {
            this.geohashCOnfig = applicationConfig.getGeohashConfig();
        }
        return this.geohashCOnfig;
    }
}
